package com.lwl.concurrency.synchr;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**<pre>
 * 如果在系统中,读操作次数远远大于写操作,则读写锁就可以发挥最大的功效,提升系统性能
 * 注意这里的 value 是一个基本数据类型int,属于值传递,
 * 所以在读线程先执行的情况下,写线程写完以后,线程读取的数据并没有被改变,(不可变对象也不会被改变)
 * 如果是非不可变对象的引用类型,那么写线程写完以后,之前读线程读取到的值被改变
 * 解决办法: 读线程的读取的返回值采用对象深拷贝的方式解决问题.见ReadWriteLockDemo2
 * </pre>
 * @author liwenlong - 2018/3/28 9:09
 */
public class ReadWriteLockDemo {
    private static final Lock lock = new ReentrantLock();
    private static final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private static final Lock readLock = readWriteLock.readLock();
    private static final Lock writeLock = readWriteLock.writeLock();
    private int value;

    public int handleRead(Lock lock) throws InterruptedException {
        try {
            lock.lock(); //模拟读操作,读操作越耗时,写锁的优势就越明显
            Thread.sleep(1000);
            return value;
        } finally {
            lock.unlock();
        }
    }

    public void handleWrite(Lock lock, int index) throws InterruptedException {
        try {
            lock.lock(); //模拟写操作
            Thread.sleep(1000);
            value = index;
            System.out.println(lock.getClass().getName()+",写入:"+index);
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ReadWriteLockDemo demo = new ReadWriteLockDemo();
        Runnable readRunnable = () -> {
            try {
                int o = demo.handleRead(readLock);
                Thread.sleep(1000);
                System.out.println("读锁读取:" + o);
                int i = demo.handleRead(lock);
                System.out.println("重入锁读取:" + i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        for (int i = 0; i < 18; i++) {
            new Thread(readRunnable).start();
        }

        for (int i = 18; i < 20; i++) {
            new Thread(new WriteRunnable(demo, i)).start();
        }

    }

    static class WriteRunnable implements Runnable {
        ReadWriteLockDemo demo;
        int index;

        public WriteRunnable(ReadWriteLockDemo demo, int index) {
            this.demo = demo;
            this.index = index;
        }

        @Override
        public void run() {
            try {
                demo.handleWrite(writeLock, index);
                demo.handleWrite(lock, index + 5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


}
